<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=1, minimum-scale=1, maximum-scale=1">
<meta name="renderer" content="webkit">
<meta name="google" value="notranslate">
<meta name="robots" content="index,follow">


<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Akkuman">
<meta name="twitter:description" content="Akkuman的技术博客">
<meta name="twitter:image:src" content="http://127.0.0.1:8000/images/avatar.png">

<meta property="og:url" content="http://127.0.0.1:8000">
<meta property="og:title" content="Akkuman">
<meta property="og:description" content="Akkuman的技术博客">
<meta property="og:site_name" content="Akkuman">
<meta property="og:image" content="http://127.0.0.1:8000/images/avatar.png">
<meta property="og:type" content="website">
<meta name="robots" content="noodp">

<meta itemprop="name" content="Akkuman">
<meta itemprop="description" content="Akkuman的技术博客">
<meta itemprop="image" content="http://127.0.0.1:8000/images/avatar.png">

<link rel="canonical" href="http://127.0.0.1:8000">

<link rel="shortcut icon" href="/favicon.png">
<link rel="apple-itouch-icon" href="/favicon.png">

<link type="text/css" rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link type="text/css" rel="stylesheet" href="/bundle/css/prism.css">
<link type="text/css" rel="stylesheet" href="/bundle/css/zoom.css">
<link type="text/css" rel="stylesheet" href="/bundle/css/main.css">
<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>



<script>var cPlayers = [];var cPlayerOptions = [];</script>


<script type="text/javascript">
    var timeSinceLang = {
        year: '年前',
        month: '个月前',
        day: '天前',
        hour: '小时前',
        minute: '分钟前',
        second: '秒前'
    };
    var root = '';
</script>


        <meta name="keywords" content="ASM,读书笔记,Windows,汇编(ASM),">
        <meta name="description" content="Win32汇编学习(6)：键盘输入消息">
        <meta name="author" content="Akkuman">
        <title>Win32汇编学习(6)：键盘输入消息</title>
    </head>
    <body>
        
        <header id="header" class="clearfix">
  <div class="container-fluid">
      <div class="row">
          <div class="logo">
              <div class="header-logo">
                <script>
                  var getwbclass = function() {
                    var wbclass = ['b', 'w'];
                    return wbclass[Math.floor(Math.random()*wbclass.length)];
                  }
                  var sitetitle = "Akkuman";
                  for (i in sitetitle) {
                    document.write('<a href="/"><span class="' + getwbclass() + ' titlechar">' + sitetitle.charAt(i) + '</span></a>');
                  }          
                  
                </script>
                
                <a id="btn-menu" href="javascript:isMenu();">
                    <span class="b">·</span>
                </a>
                <a href="javascript:isMenu1();">
                    <span id="menu-1" class="bf">1</span>
                </a>
                <a href="javascript:isMenu2();">
                    <span id="menu-2" class="bf">2</span>
                </a>
                <a href="javascript:isMenu3();">
                    <span id="menu-3" class="bf">3</span>
                </a>
              </div>
              <div id="menu-page">
                <a href="/archive.html"><li>归档</li></a>
                <a href="/tag.html"><li>标签</li></a>
                
                <a href="/atom.xml"><li>订阅</li></a>
                
                <a href="about.html"><li>关于</li></a>
              </div>
              <div id="search-box">
                  <div id="search">
                      <input autocomplete="off" type="text" name="s" id="menu-search" placeholder="搜索..." data-root="" />
                  </div>
              </div>
          </div>
      </div>
  </div>
  </header>
        <div id="body" class="clearfix">
            <div class="container-fluid">
                <div class="row">
                    <div id="main" class="col-12 clearfix" role="main">
                        <article class="posti" itemscope itemtype="http://schema.org/BlogPosting">
                            <h1 class="post-title" itemprop="name headline">Win32汇编学习(6)：键盘输入消息</h1>
                            <div class="post-meta">
                                <p>
                                    Written by <a itemprop="name" href="/about.me.html" rel="author">Akkuman</a> with ♥ on <time datetime="1518083639" itemprop="datePublished"></time> in <a href="/tag/ASM/index.html">ASM </a><a href="/tag/%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0/index.html">读书笔记 </a><a href="/tag/Windows/index.html">Windows </a><a href="/tag/%e6%b1%87%e7%bc%96%28ASM%29/index.html">汇编(ASM) </a>
                                </p>
                            </div>
                            <div class="post-content" itemprop="articleBody">
                                <p>这次，我们将要学习WINDOWS程序是如何处理键盘消息的。</p>

<h2>理论：</h2>

<p>因为大多数的PC只有一个键盘，所以所有运行中的WINDOWS程序必须共用它。<strong>WINDOWS 将负责把击键消息送到具有输入焦点的那个应用程序中去</strong>。尽管屏幕上可能同时有几个应用程序窗口，但一个时刻仅有一个窗口有输入焦点。有输入焦点的那个应用程序的标题条总是高亮度显示的。 实际上您可以从两个角度来看键盘消息：一是您可以把它看成是一大堆的按键消息的集合，在这种情况下，当您按下一个键时，WINDOWS就会发送一个 <code>WM_KEYDOWN</code> 给有输入焦点的那个应用程序，提醒它有一个键被按下。当您释放键时，WINDOWS又会发送一个 <code>WM_KYEUP</code> 消息，告诉有一个键被释放。您把每一个键当成是一个按钮；另一种情况是：您可以把键盘看成是字符输入设备。当您按下“a”键时，WINDOWS发送一个 <code>WM_CHAR</code> 消息给有输入焦点的应用程序，告诉它“a”键被按下。实际上WINDOWS 内部发送 <code>WM_KEYDOWN</code> 和 <code>WM_KEYUP</code> 消息给有输入焦点的应用程序，而这些消息将通过调用 <code>TranslateMessage</code> 翻译成 <code>WM_CHAR</code> 消息。WINDOWS窗口过程函数将决定是否处理所收到的消息，一般说来您不大会去处理 <code>WM_KEYDOWN</code> 、 <code>WM_KEYUP</code> 消息，在消息循环中 <code>TranslateMessage</code> 函数会把上述消息转换成 <code>WM_CHAR</code> 消息。这次学习中将只处理 <code>WM_CHAR</code>。</p>

<h2>例子：</h2>

<pre><code>.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib

.data
ClassName db &quot;SimpleWinClass&quot;,0
AppName   db &quot;Our Fourth Window&quot;,0
char WPARAM 20h

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.code
start:
invoke GetModuleHandle,NULL
mov    hInstance,eax
invoke GetCommandLine
mov    CommandLine,eax
invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov wc.cbSize,SIZEOF WNDCLASSEX
    mov wc.style,CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc,OFFSET WndProc
    mov wc.cbClsExtra,NULL
    mov wc.cbWndExtra,NULL
    push hInst
    pop wc.hInstance
    mov wc.hbrBackground,COLOR_WINDOW+1
    mov wc.lpszMenuName,NULL
    mov wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov wc.hIcon,eax
    mov wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov wc.hCursor,eax
    invoke RegisterClassEx,ADDR wc
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
                        WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,\
                        CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL
    mov hwnd,eax
    invoke ShowWindow,hwnd,SW_SHOWNORMAL
    invoke UpdateWindow,hwnd
    .while TRUE
        invoke GetMessage,ADDR msg,NULL,0,0
        .break .if (!eax)
        invoke TranslateMessage,ADDR msg
        invoke DispatchMessage,ADDR msg
    .endw
    mov eax,msg.wParam
    ret

WinMain endp 

WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    
    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    
    .if uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .elseif uMsg==WM_CHAR
        push wParam
        pop  char
        invoke InvalidateRect,hWnd,NULL,TRUE
    .elseif uMsg==WM_PAINT
        invoke BeginPaint,hWnd,ADDR ps
        mov    hdc,eax
        invoke TextOut,hdc,0,0,ADDR char,1
        invoke EndPaint,hWnd,ADDR ps
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret

WndProc endp
end start
</code></pre>

<h2>分析：</h2>

<pre><code>char WPARAM 20h
</code></pre>

<p>这个变量将保存从键盘接收到的字符。因为它是在窗口过程中通过WPARAM型变量传送的，所以我们简单地把它定义为WPARAM型。<strong>由于我们的窗口在初次刷新时(也即刚被创建的那一次)是没有键盘输入的所以我们把他设成空格符（20h），这样显示时您就什么都看不见。</strong></p>

<pre><code>.ELSEIF uMsg==WM_CHAR 
    push wParam 
    pop  char 
    invoke InvalidateRect, hWnd,NULL,TRUE 
</code></pre>

<p>这一段是用来处理<code>WM_CHAR</code>消息的。它把接收到的字符放入变量<code>char</code>中，接着调用<code>InvalidateRect</code>，而InvalidateRect使得窗口的客户区无效，这样它会发出WM_PAINT消息，而WM_PAINT消息迫使WINDOWS重新绘制它的客户区。该函数的语法如下：</p>

<pre><code>InvalidateRect proto hWnd:HWND, lpRect:DWORD, bErase:DWORD 
</code></pre>

<p><code>lpRect</code>是指向客户区我们想要其无效的一个正方形结构体的指针。如果该值等于<code>NULL</code>，则整个客户区都无效；布尔值<code>bErase</code>告诉WINDOWS是否擦除背景，如果是TRUE，则WINDOWS在调用BeginPaint函数时把背景擦掉。 所以我们此处的做法是：<strong>我们将保存所有有关重绘客户区的数据，然后发送<code>WM_PAINT</code>消息(通过<code>InvalidateRect</code>)，处理该消息的程序段然后根据相关数据重新绘制客户区。实际上我们完全可以通过调用 <code>GetDC</code> 获得设备上下文句柄，然后绘制字符，然后再调用<code>ReleaseDC</code>释放设备上下文句柄，毫无疑问这样也能在客户区绘制出正确的字符。但是如果这之后接收到<code>WM_PAINT</code>消息要处理时，客户区会重新刷新，而我们这稍前所绘制的字符就会消失掉。所以为了让字符一直正确地显示，就必须把它们放到<code>WM_PAINT</code>的处理过程中处理。而在本消息处理中发送<code>WM_PAINT</code>消息即可。</strong></p>

<pre><code>invoke TextOut,hdc,0,0,ADDR char,1 
</code></pre>

<p><strong>在调用<code>InvalidateRect</code>时，<code>WM_PAINT</code>消息被发送到了WINDOWS窗口处理过程，程序流程转移到处理<code>WM_PAINT</code>消息的程序段</strong>，然后调用<code>BeginPaint</code>得到设备上下文的句柄，再调用<code>TextOut</code>在客户区的（0，0）处输出保存的按键字符。这样无论您按什么键都能在客户区的左上角显示，不仅如此，无论您怎么缩放窗口（迫使WINDOWS重新绘制它的客户区），字符都会在正确的地方显示，所以<strong>必须把所有重要的绘制动作都放到处理<code>WM_PAINT</code>消息的程序段中去</strong>。</p>

                            </div>
                            <div style="display:block;" class="clearfix">
                                <section style="float:left;">
                                    <span itemprop="keywords" class="tags">
                                        tag(s): <a href="/tag/ASM/index.html">ASM </a><a href="/tag/%e8%af%bb%e4%b9%a6%e7%ac%94%e8%ae%b0/index.html">读书笔记 </a><a href="/tag/Windows/index.html">Windows </a><a href="/tag/%e6%b1%87%e7%bc%96%28ASM%29/index.html">汇编(ASM) </a>
                                    </span>
                                </section>
                                <section style="float:right;">
                                    <span><a id="btn-comments" href="javascript:isComments();">show comments</a></span> · <span><a href="javascript:goBack();">back</a></span> · 
                                    <span><a href="/">home</a></span>
                                </section>
                            </div>
                            



<div id="comments" class="gen">
    <script>
        document.write('<section id="disqus_thread"></section>');
        var site_comment_load = function disqus() {
            var d = document, s = d.createElement('script');
            s.src = '//Akkum4n.disqus.com/embed.js';
            s.setAttribute('data-timestamp', +new Date());
            (d.head || d.body).appendChild(s);
        }
    </script>
</div>

                        </article>
                    </div>
                </div>
            </div>
        </div>
        <footer id="footer" role="contentinfo">
    <div class="container-fluid">
        <div class="row">
        <div class="col-12">
            &copy; 
            <script type="text/javascript">
                document.write(new Date().getFullYear());
            </script>
            <a href="/">Akkuman</a>.
            Using <a target="_blank" href="http://www.chole.io/">Ink</a> & <a target="_blank" href="/">Story</a>.
        </div>
        </div>
    </div>
</footer>

<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
<script src="/bundle/js/prism.js"></script>
<script src="/bundle/js/zoom-vanilla.min.js"></script>
<script src="/bundle/js/main.js"></script>

<script>
    window.onload=function(){
        if (window.location.hash!='') {
          var i=window.location.hash.indexOf('#comment');
          var ii=window.location.hash.indexOf('#respond-post');
          if (i != '-1' || ii != '-1') {
            document.getElementById('btn-comments').innerText='hide comments';
            document.getElementById('comments').style.display='block';
          }
        }
    }

    function isMenu(){
        if(document.getElementById('menu-1').style.display=='inline'||document.getElementById('menu-1').style.display=='block'){
            $('#search-box').fadeOut(200);
            $('#menu-page').fadeOut(200);
            $('#menu-1').fadeOut(500);
            $('#menu-2').fadeOut(400);
            $('#menu-3').fadeOut(300);
        } else {
            $('#menu-1').fadeIn(150);
            $('#menu-2').fadeIn(150);
            $('#menu-3').fadeIn(150);
        }
    }

    function isMenu1(){
        if(document.getElementById('menu-page').style.display=='block'){
            $('#menu-page').fadeOut(300);
        } else {
            $('#menu-page').fadeIn(300);
        }
    }

    function isMenu2(){
        if(document.getElementById('torTree')){
            if(document.getElementById('torTree').style.display=='block'){
                $('#torTree').fadeOut(300);
            } else {
                $('#torTree').fadeIn(300);
            }
        }
    }

    function isMenu3(){
        if(document.getElementById('search-box').style.display=='block'){
            $('#search-box').fadeOut(300);
        } else {
            $('#search-box').fadeIn(300);
        }
    }

    function isComments(){
        if(document.getElementById('btn-comments').innerText=='show comments'){
            document.getElementById('btn-comments').innerText='hide comments';
            document.getElementById('comments').style.display='block';
            site_comment_load();
        } else {
            document.getElementById('btn-comments').innerText='show comments';
            document.getElementById('comments').style.display='none';
        }
    }

    function Search404(){
        $('#menu-1').fadeIn(150);
        $('#menu-2').fadeIn(150);
        $('#menu-3').fadeIn(150);
        $('#search-box').fadeIn(300);
    }

    function goBack(){
        window.history.back();
    }
</script>


<script async>
"use strict";
(function(){
var cp = function(){
    var len = cPlayerOptions.length;
    for(var i=0;i<len;i++){
        var element = document.getElementById('player' + cPlayerOptions[i]['id'])
        while (element.hasChildNodes()) {
            element.removeChild(element.firstChild);
        };
        cPlayers[i] = new cPlayer({
            element: element,
            list: cPlayerOptions[i]['list'],
            });
    };
    cPlayers = [];cPlayerOptions = [];
};
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "https://cdn.bootcss.com/cplayer/3.2.1/cplayer.js";
script.async = true;
if(script.readyState){  
    script.onreadystatechange = function(){
        if (script.readyState == "loaded" ||
            script.readyState == "complete"){
            script.onreadystatechange = null;
            cp();
        }
    };
}else{  
    script.onload = function(){
        cp();
    };
}
document.head.appendChild(script);
})();
</script>

    </body>
</html>
